﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    //TODO: relativen modus hinzufügen

    //TODO: als Scanline blob Extraktor algorithmus benutzen,
    //dazu müssen nur die Scanlines mit ähnlichen Durchschnittsfarben die sich berühren,
    //gemerged werden. Interessante Frage dazu: Regions oder Rectangles benutzen?
    //Regions sind eigentlich besser, da beliebige Formen entstehen können.
    /// <summary>
    /// 
    /// </summary>
    public class FastSegmentation2 : BaseImageFilter
    {
        protected FASTSEGMENTATION_MODE MODE = FASTSEGMENTATION_MODE.BOTH;
        protected int r = 0;
        protected int g = 0;
        protected int b = 0;

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation2"/> class.
        /// </summary>
        public FastSegmentation2()
            : this(FASTSEGMENTATION_MODE.BOTH, 10, 10, 10)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation2"/> class.
        /// </summary>
        /// <param name="_r">The _r.</param>
        /// <param name="_g">The _g.</param>
        /// <param name="_b">The _b.</param>
        public FastSegmentation2(int _r, int _g, int _b)
            : this(FASTSEGMENTATION_MODE.BOTH, _r, _g, _b)
        {
            r = _r;
            g = _g;
            b = _b;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation2"/> class.
        /// </summary>
        /// <param name="_MODE">The _ MODE.</param>
        /// <param name="_r">The _r.</param>
        /// <param name="_g">The _g.</param>
        /// <param name="_b">The _b.</param>
        public FastSegmentation2(FASTSEGMENTATION_MODE _MODE, int _r, int _g, int _b)
        {
            MODE = FASTSEGMENTATION_MODE.BOTH;
            r = _r;
            g = _g;
            b = _b;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FastSegmentation2"/> class.
        /// </summary>
        /// <param name="_fastSegmentation2">The _fast segmentation2.</param>
        internal FastSegmentation2(FastSegmentation2 _fastSegmentation2)
        {
            this.MODE = _fastSegmentation2.MODE;
            this.r = _fastSegmentation2.r;
            this.g = _fastSegmentation2.g;
            this.b = _fastSegmentation2.b;
        }

        /// <summary>
        /// Gets or sets the fast segmentation mode.
        /// </summary>
        /// <value>The fast segmentation mode.</value>
        public FASTSEGMENTATION_MODE FastSegmentationMode
        {
            get { return MODE; }
            set { MODE = value; }
        }

        /// <summary>
        /// Gets or sets the R.
        /// </summary>
        /// <value>The R.</value>
        public int R
        {
            get { return r; }
            set { r = value; }
        }

        /// <summary>
        /// Gets or sets the G.
        /// </summary>
        /// <value>The G.</value>
        public int G
        {
            get { return g; }
            set { g = value; }
        }

        /// <summary>
        /// Gets or sets the B.
        /// </summary>
        /// <value>The B.</value>
        public int B
        {
            get { return b; }
            set { b = value; }
        }

        /// <summary>
        /// This is a fast color segmentation on a horizontal line base
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        /// <remarks>Interesting feature to add: relative color tolerance from previous pixel
        /// Maybe I should also add a vertical mode?
        /// And what might happen if both is combined?</remarks>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            Horizontal(_bitmap, r, g, b);
            Vertical(_bitmap, r, g, b);
            return _bitmap;
        }

        /// <summary>
        /// Horizontals the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="r">The r.</param>
        /// <param name="g">The g.</param>
        /// <param name="b">The b.</param>
        /// <returns></returns>
        public static List<List<Range>> Horizontal(UnsafeBitmap _bitmap, int r, int g, int b)
        {
            UnsafeBitmap bitmap = _bitmap;

            List<List<Range>> aScanLine = new List<List<Range>>();

            List<Color> aColor = new List<Color>();
            aColor.Add(bitmap.GetPixel(0, 0));

            int nCount = 1;
            int x = 0; int y = 0;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            Color colorStart = Color.Transparent;
            Color colorNext = Color.Transparent;
            
            for (y = 0; y < nHeight; y++)
            {
                List<Range> aList = new List<Range>();
                aColor.Add(bitmap.GetPixel(0, y));
                nCount = 1;

                for (x = 0; x < nWidth - 1; x++)
                {
                    colorStart = bitmap.GetPixel(x, y);

                    colorNext = bitmap.GetPixel(x, y);

                    int nStartX = 0;
                    int nEndX = 0;

                    //Found something interesting?
                    if (ColorFunctions.IsInColorTolerance(colorStart, colorNext, r, g, b))
                    {
                        //if so, remember position and search for the end of the interesting pixel formation
                        nStartX = x;
                        while (ColorFunctions.IsInColorTolerance(colorStart, colorNext, r, g, b))
                        {
                            colorNext = bitmap.GetPixel(x, y);
                            aColor.Add(colorNext);
                            x++;
                            if (x >= nWidth)
                            {
                                break;
                            }
                            nCount++;
                        }

                        //end found? then calculate average color and add this range to the scanlines 
                        nEndX = x;
                        Color colorAvg = ColorFunctions.AverageColor(aColor);
                        aColor.Clear();

                        Range range = new Range(nStartX, nEndX, y, colorAvg);
                        aList.Add(range);
                    }
                    nCount++;
                    x++;
                }
                aScanLine.Add(aList);
            }

            //Replace values by new Colors (the colors have been derived from scanning
            //in the previous loop

            _bitmap.UnlockImage();
            Graphics graphics = Graphics.FromImage(_bitmap.InternalBitmap);
            for (y = 0; y < aScanLine.Count; y++)
            {
                List<Range> aRange = (List<Range>)aScanLine[y];
                foreach (Range range in aRange)
                {
                    graphics.DrawLine(new Pen(range.Color), range.StartX, y, range.EndX, y);
                }
            }
            _bitmap.LockImage();
            return aScanLine;
        }

        /// <summary>
        /// Verticals the specified _bitmap.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="r">The r.</param>
        /// <param name="g">The g.</param>
        /// <param name="b">The b.</param>
        /// <returns></returns>
        public static List<List<Range>> Vertical(UnsafeBitmap _bitmap, int r, int g, int b)
        {
            List<List<Range>> aScanLine = new List<List<Range>>();

            List<Color> aColor = new List<Color>();
            aColor.Add(_bitmap.GetPixel(0, 0));

            int nCount = 1;
            int x = 0; int y = 0;

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            for (x = 0; x < nWidth; x++)
            {
                List<Range> aList = new List<Range>();
                aColor.Add(_bitmap.GetPixel(x, 0));
                nCount = 1;

                for (y = 0; y < nHeight - 1; y++)
                {
                    Color colorStart = _bitmap.GetPixel(x, y);

                    y++;
                    Color colorNext = _bitmap.GetPixel(x, y);

                    int nStartY = 0;
                    int nEndY = 0;

                    //Found something interesting?
                    if (ColorFunctions.IsInColorTolerance(colorStart, colorNext, r, g, b))
                    {
                        //if so, remember position and search for the end of the interesting pixel formation
                        nStartY = y;
                        while (ColorFunctions.IsInColorTolerance(colorStart, colorNext, r, g, b))
                        {
                            colorNext = _bitmap.GetPixel(x, y);
                            aColor.Add(colorNext);
                            y++;
                            if (y >= _bitmap.Height)
                            {
                                break;
                            }
                            nCount++;
                        }

                        //end found? then calculate average color and add this range to the scanlines 
                        nEndY = y;
                        Color colorAvg = ColorFunctions.AverageColor(aColor);
                        aColor.Clear();

                        Range range = new Range(nStartY, nEndY, x, colorAvg);
                        aList.Add(range);
                    }
                    nCount++;
                }
                aScanLine.Add(aList);
            }

            //Replace values by new Colors (the colors have been derived from scanning
            //in the previous loop
            _bitmap.UnlockImage();
            Graphics graphics = Graphics.FromImage(_bitmap.InternalBitmap);
            for (x = 0; x < aScanLine.Count; x++)
            {
                List<Range> aRange = (List<Range>)aScanLine[x];
                foreach (Range range in aRange)
                {
                    graphics.DrawLine(new Pen(range.Color), x, range.StartX, x, range.EndX);
                }
            }
            _bitmap.LockImage();
            return aScanLine;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new FastSegmentation2(this);
        }
    }
}
